package game;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.util.ArrayList;

import loading.ImageLoader;
import main.GameList;
import main.Inventory;
import math.Equations;
import math.Vector2D;

import entity.DynamicEntity;
import entity.Player;
import entity.immobile.Door;
import entity.immobile.Solid;
import entity.immobile.Wall;
import entity.item.Item;
import entity.item.Key;

import gamestate.State;
import gamestate.StateStack;
import gui.user.User;

public class InGame extends State
{
	private Player player;
	private GameList list;
	private ImageLoader floor;
	private final int HORIZ_ADJUST = 256, VERT_ADJUST = 128;
	
	public InGame(StateStack s, User u) 
	{
		super(s, u);
		list = new GameList();
		floor = new ImageLoader("img/floor.png");
		player = new Player(new Point2D.Double(512, 300), list, new ImageLoader("img/player.png"), new Inventory());
		Key key1 = new Key(new Point2D.Double(500, 500), list, new ImageLoader("img/key.png"));
		Door door1 = new Door(new Point2D.Double(439, 400), list, new ImageLoader("img/door.png", Math.toRadians(90)), key1);
		list.add(key1);
		list.add(player);
		list.add(door1);
		list.add(new Wall(new Point2D.Double(400, 400), list, new ImageLoader("img/wall.png"), 5, 1));
		list.add(new Wall(new Point2D.Double(464, 400), list, new ImageLoader("img/wall.png"), 1, 5));
	}

	@Override
	public void render(Graphics2D g)
	{
		g.setColor(Color.gray);
		g.fillRect(0, 0, 1024, 768);
		g.setColor(Color.black);
		for (int i = 0; i < 512; i+=32)
		{
			for (int j = 0; j < 512; j+=32)
			{
				floor.draw(g, AffineTransform.getTranslateInstance(i+HORIZ_ADJUST, j+VERT_ADJUST));
			}
		}
		list.render(g);
	}

	@Override
	public void update(double duration) 
	{
		if (getUser().isPressed(KeyEvent.VK_ESCAPE))
		{
			System.exit(0);
		}
		//see what side of a wall a player is colliding with
		boolean[] walls = atWall(player);
		//get nearby solid objects
		ArrayList<Solid> collideWall = list.getWithinRange(player.getPosition(), player.getRadius(), Solid.class);
		//if one of the movement buttons is pressed, move that directions
		//if it is not being pressed or a wall is being hit on that side, don't move that direction
		if (getUser().isPressed(KeyEvent.VK_W) && !walls[0] && !checkWall(collideWall, 2))
		{
			player.moveUp();
		}
		else
		{
			player.stopUp();
		}
		if (getUser().isPressed(KeyEvent.VK_S) && !walls[2] && !checkWall(collideWall, 0))
		{
			player.moveDown();
		}
		else
		{
			player.stopDown();
		}
		if (getUser().isPressed(KeyEvent.VK_A) && !walls[3] && !checkWall(collideWall, 1))
		{
			player.moveLeft();
		}
		else
		{
			player.stopLeft();
		}
		if (getUser().isPressed(KeyEvent.VK_D) && !walls[1] && !checkWall(collideWall, 3))
		{
			player.moveRight();
		}
		else
		{
			player.stopRight();
		}
		//if E is pressed, check for nearby items
		if (getUser().isPressed(KeyEvent.VK_E))
		{
			checkForItems();
			checkForDoors();
		}
		list.update(duration);
		player.setAngle(Equations.pointToPointAngle(player.getPosition(), getUser().getMouseMove()));
	}
	//returns booleans for each map boundary, 0 = top, 1 = right, 2 = bottom, 3 = left
	private boolean[] atWall(DynamicEntity ent)
	{
		boolean[] walls = new boolean[4];
		walls[3] = ent.getPosition().getX()-ent.getRadius() <= HORIZ_ADJUST;
		walls[1] = ent.getPosition().getX()+ent.getRadius() >= HORIZ_ADJUST+512;
		walls[0] = ent.getPosition().getY()-ent.getRadius() <= VERT_ADJUST;
		walls[2] = ent.getPosition().getY()+ent.getRadius() >= VERT_ADJUST+512;
		return walls;
	}
	//checks the walls in the array list to side if the side is being collided
	private boolean checkWall(ArrayList<Solid> walls, int side)
	{
		for (Solid wall : walls)
		{
			if (wall.getCollisionSide(player) == side)
				return true;
		}
		return false;
	}
	private void checkForItems()
	{
		//get nearby items
		ArrayList<Item> items = list.getWithinRange(player.getPosition(), player.getRadius()+Player.USE_RANGE, Item.class);
		//if there are nearby items, see if one can be picked up
		for (Item i : items)
		{
			//if the player is facing an item, pick it uo
			if (player.facing(i))
			{
				player.pickUp(i);
				list.remove(i);
			}
		}
	}
	private void checkForDoors()
	{
		ArrayList<Door> doors = list.getWithinRange(player.getPosition(), player.getRadius()+Player.USE_RANGE, Door.class);
		for (Door d : doors)
		{
			//if the player is facing the door and has the correct key,
			//unlock the door and dispose of the key
			if (player.facing(d) && player.has(d.getKey()))
			{
				player.dispose(d.getKey());
				d.unlock();
			}
		}
	}
}
